import 'dart:typed_data';

import 'package:pointycastle/src/ufixnum.dart';

class DesBase {
  static final BLOCK_SIZE = 8;

  //static final List<int> bytebit = [128, 64, 32, 16, 8, 4, 2, 1];
  static final List<int> bytebit = [0x80, 0x40, 0x20, 0x10, 0x8, 0x4, 0x2, 0x1];

  static final List<int> bigbyte = [
    0x800000,
    0x400000,
    0x200000,
    0x100000,
    0x80000,
    0x40000,
    0x20000,
    0x10000,
    0x8000,
    0x4000,
    0x2000,
    0x1000,
    0x800,
    0x400,
    0x200,
    0x100,
    0x80,
    0x40,
    0x20,
    0x10,
    0x8,
    0x4,
    0x2,
    0x1
  ];

  /*
     * Use the key schedule specified in the Standard (ANSI X3.92-1981).
     */

  static final Uint8List pc1 = Uint8List.fromList([
    56,
    48,
    40,
    32,
    24,
    16,
    8,
    0,
    57,
    49,
    41,
    33,
    25,
    17,
    9,
    1,
    58,
    50,
    42,
    34,
    26,
    18,
    10,
    2,
    59,
    51,
    43,
    35,
    62,
    54,
    46,
    38,
    30,
    22,
    14,
    6,
    61,
    53,
    45,
    37,
    29,
    21,
    13,
    5,
    60,
    52,
    44,
    36,
    28,
    20,
    12,
    4,
    27,
    19,
    11,
    3
  ]);

  static final Uint8List totrot = Uint8List.fromList(
      [1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28]);

  static final Uint8List pc2 = Uint8List.fromList([
    13,
    16,
    10,
    23,
    0,
    4,
    2,
    27,
    14,
    5,
    20,
    9,
    22,
    18,
    11,
    3,
    25,
    7,
    15,
    6,
    26,
    19,
    12,
    1,
    40,
    51,
    30,
    36,
    46,
    54,
    29,
    39,
    50,
    44,
    32,
    47,
    43,
    48,
    38,
    55,
    33,
    52,
    45,
    41,
    49,
    35,
    28,
    31
  ]);

  static final List<int> SP1 = [
    0x01010400,
    0x00000000,
    0x00010000,
    0x01010404,
    0x01010004,
    0x00010404,
    0x00000004,
    0x00010000,
    0x00000400,
    0x01010400,
    0x01010404,
    0x00000400,
    0x01000404,
    0x01010004,
    0x01000000,
    0x00000004,
    0x00000404,
    0x01000400,
    0x01000400,
    0x00010400,
    0x00010400,
    0x01010000,
    0x01010000,
    0x01000404,
    0x00010004,
    0x01000004,
    0x01000004,
    0x00010004,
    0x00000000,
    0x00000404,
    0x00010404,
    0x01000000,
    0x00010000,
    0x01010404,
    0x00000004,
    0x01010000,
    0x01010400,
    0x01000000,
    0x01000000,
    0x00000400,
    0x01010004,
    0x00010000,
    0x00010400,
    0x01000004,
    0x00000400,
    0x00000004,
    0x01000404,
    0x00010404,
    0x01010404,
    0x00010004,
    0x01010000,
    0x01000404,
    0x01000004,
    0x00000404,
    0x00010404,
    0x01010400,
    0x00000404,
    0x01000400,
    0x01000400,
    0x00000000,
    0x00010004,
    0x00010400,
    0x00000000,
    0x01010004
  ];

  static final List<int> SP2 = [
    0x80108020,
    0x80008000,
    0x00008000,
    0x00108020,
    0x00100000,
    0x00000020,
    0x80100020,
    0x80008020,
    0x80000020,
    0x80108020,
    0x80108000,
    0x80000000,
    0x80008000,
    0x00100000,
    0x00000020,
    0x80100020,
    0x00108000,
    0x00100020,
    0x80008020,
    0x00000000,
    0x80000000,
    0x00008000,
    0x00108020,
    0x80100000,
    0x00100020,
    0x80000020,
    0x00000000,
    0x00108000,
    0x00008020,
    0x80108000,
    0x80100000,
    0x00008020,
    0x00000000,
    0x00108020,
    0x80100020,
    0x00100000,
    0x80008020,
    0x80100000,
    0x80108000,
    0x00008000,
    0x80100000,
    0x80008000,
    0x00000020,
    0x80108020,
    0x00108020,
    0x00000020,
    0x00008000,
    0x80000000,
    0x00008020,
    0x80108000,
    0x00100000,
    0x80000020,
    0x00100020,
    0x80008020,
    0x80000020,
    0x00100020,
    0x00108000,
    0x00000000,
    0x80008000,
    0x00008020,
    0x80000000,
    0x80100020,
    0x80108020,
    0x00108000
  ];

  static final List<int> SP3 = [
    0x00000208,
    0x08020200,
    0x00000000,
    0x08020008,
    0x08000200,
    0x00000000,
    0x00020208,
    0x08000200,
    0x00020008,
    0x08000008,
    0x08000008,
    0x00020000,
    0x08020208,
    0x00020008,
    0x08020000,
    0x00000208,
    0x08000000,
    0x00000008,
    0x08020200,
    0x00000200,
    0x00020200,
    0x08020000,
    0x08020008,
    0x00020208,
    0x08000208,
    0x00020200,
    0x00020000,
    0x08000208,
    0x00000008,
    0x08020208,
    0x00000200,
    0x08000000,
    0x08020200,
    0x08000000,
    0x00020008,
    0x00000208,
    0x00020000,
    0x08020200,
    0x08000200,
    0x00000000,
    0x00000200,
    0x00020008,
    0x08020208,
    0x08000200,
    0x08000008,
    0x00000200,
    0x00000000,
    0x08020008,
    0x08000208,
    0x00020000,
    0x08000000,
    0x08020208,
    0x00000008,
    0x00020208,
    0x00020200,
    0x08000008,
    0x08020000,
    0x08000208,
    0x00000208,
    0x08020000,
    0x00020208,
    0x00000008,
    0x08020008,
    0x00020200
  ];

  static final List<int> SP4 = [
    0x00802001,
    0x00002081,
    0x00002081,
    0x00000080,
    0x00802080,
    0x00800081,
    0x00800001,
    0x00002001,
    0x00000000,
    0x00802000,
    0x00802000,
    0x00802081,
    0x00000081,
    0x00000000,
    0x00800080,
    0x00800001,
    0x00000001,
    0x00002000,
    0x00800000,
    0x00802001,
    0x00000080,
    0x00800000,
    0x00002001,
    0x00002080,
    0x00800081,
    0x00000001,
    0x00002080,
    0x00800080,
    0x00002000,
    0x00802080,
    0x00802081,
    0x00000081,
    0x00800080,
    0x00800001,
    0x00802000,
    0x00802081,
    0x00000081,
    0x00000000,
    0x00000000,
    0x00802000,
    0x00002080,
    0x00800080,
    0x00800081,
    0x00000001,
    0x00802001,
    0x00002081,
    0x00002081,
    0x00000080,
    0x00802081,
    0x00000081,
    0x00000001,
    0x00002000,
    0x00800001,
    0x00002001,
    0x00802080,
    0x00800081,
    0x00002001,
    0x00002080,
    0x00800000,
    0x00802001,
    0x00000080,
    0x00800000,
    0x00002000,
    0x00802080
  ];

  static final List<int> SP5 = [
    0x00000100,
    0x02080100,
    0x02080000,
    0x42000100,
    0x00080000,
    0x00000100,
    0x40000000,
    0x02080000,
    0x40080100,
    0x00080000,
    0x02000100,
    0x40080100,
    0x42000100,
    0x42080000,
    0x00080100,
    0x40000000,
    0x02000000,
    0x40080000,
    0x40080000,
    0x00000000,
    0x40000100,
    0x42080100,
    0x42080100,
    0x02000100,
    0x42080000,
    0x40000100,
    0x00000000,
    0x42000000,
    0x02080100,
    0x02000000,
    0x42000000,
    0x00080100,
    0x00080000,
    0x42000100,
    0x00000100,
    0x02000000,
    0x40000000,
    0x02080000,
    0x42000100,
    0x40080100,
    0x02000100,
    0x40000000,
    0x42080000,
    0x02080100,
    0x40080100,
    0x00000100,
    0x02000000,
    0x42080000,
    0x42080100,
    0x00080100,
    0x42000000,
    0x42080100,
    0x02080000,
    0x00000000,
    0x40080000,
    0x42000000,
    0x00080100,
    0x02000100,
    0x40000100,
    0x00080000,
    0x00000000,
    0x40080000,
    0x02080100,
    0x40000100
  ];

  static final List<int> SP6 = [
    0x20000010,
    0x20400000,
    0x00004000,
    0x20404010,
    0x20400000,
    0x00000010,
    0x20404010,
    0x00400000,
    0x20004000,
    0x00404010,
    0x00400000,
    0x20000010,
    0x00400010,
    0x20004000,
    0x20000000,
    0x00004010,
    0x00000000,
    0x00400010,
    0x20004010,
    0x00004000,
    0x00404000,
    0x20004010,
    0x00000010,
    0x20400010,
    0x20400010,
    0x00000000,
    0x00404010,
    0x20404000,
    0x00004010,
    0x00404000,
    0x20404000,
    0x20000000,
    0x20004000,
    0x00000010,
    0x20400010,
    0x00404000,
    0x20404010,
    0x00400000,
    0x00004010,
    0x20000010,
    0x00400000,
    0x20004000,
    0x20000000,
    0x00004010,
    0x20000010,
    0x20404010,
    0x00404000,
    0x20400000,
    0x00404010,
    0x20404000,
    0x00000000,
    0x20400010,
    0x00000010,
    0x00004000,
    0x20400000,
    0x00404010,
    0x00004000,
    0x00400010,
    0x20004010,
    0x00000000,
    0x20404000,
    0x20000000,
    0x00400010,
    0x20004010
  ];

  static final List<int> SP7 = [
    0x00200000,
    0x04200002,
    0x04000802,
    0x00000000,
    0x00000800,
    0x04000802,
    0x00200802,
    0x04200800,
    0x04200802,
    0x00200000,
    0x00000000,
    0x04000002,
    0x00000002,
    0x04000000,
    0x04200002,
    0x00000802,
    0x04000800,
    0x00200802,
    0x00200002,
    0x04000800,
    0x04000002,
    0x04200000,
    0x04200800,
    0x00200002,
    0x04200000,
    0x00000800,
    0x00000802,
    0x04200802,
    0x00200800,
    0x00000002,
    0x04000000,
    0x00200800,
    0x04000000,
    0x00200800,
    0x00200000,
    0x04000802,
    0x04000802,
    0x04200002,
    0x04200002,
    0x00000002,
    0x00200002,
    0x04000000,
    0x04000800,
    0x00200000,
    0x04200800,
    0x00000802,
    0x00200802,
    0x04200800,
    0x00000802,
    0x04000002,
    0x04200802,
    0x04200000,
    0x00200800,
    0x00000000,
    0x00000002,
    0x04200802,
    0x00000000,
    0x00200802,
    0x04200000,
    0x00000800,
    0x04000002,
    0x04000800,
    0x00000800,
    0x00200002
  ];

  static final List<int> SP8 = [
    0x10001040,
    0x00001000,
    0x00040000,
    0x10041040,
    0x10000000,
    0x10001040,
    0x00000040,
    0x10000000,
    0x00040040,
    0x10040000,
    0x10041040,
    0x00041000,
    0x10041000,
    0x00041040,
    0x00001000,
    0x00000040,
    0x10040000,
    0x10000040,
    0x10001000,
    0x00001040,
    0x00041000,
    0x00040040,
    0x10040040,
    0x10041000,
    0x00001040,
    0x00000000,
    0x00000000,
    0x10040040,
    0x10000040,
    0x10001000,
    0x00041040,
    0x00040000,
    0x00041040,
    0x00040000,
    0x10041000,
    0x00001000,
    0x00000040,
    0x10040040,
    0x00001000,
    0x00041040,
    0x10001000,
    0x00000040,
    0x10000040,
    0x10040000,
    0x10040040,
    0x10000000,
    0x00040000,
    0x10001040,
    0x00000000,
    0x10041040,
    0x00040040,
    0x10000040,
    0x10040000,
    0x10001000,
    0x10001040,
    0x00000000,
    0x10041040,
    0x00041000,
    0x00041000,
    0x00001040,
    0x00001040,
    0x00040040,
    0x10000000,
    0x10041000
  ];

  List<int> generateWorkingKey(bool encrypting, Uint8List key) {
    var newKey = List<int>.generate(32, (index) => 0);
    var pc1m = List<bool>.generate(56, (index) => false);
    var pcr = List<bool>.generate(56, (index) => false);

    for (var j = 0; j < 56; j++) {
      var l = pc1[j];

      pc1m[j] = ((key[shiftr32(l, 3)] & bytebit[l & 07]) != 0);
    }

    for (var i = 0; i < 16; i++) {
      int l, m, n;

      if (encrypting) {
        m = shiftl32(i, 1);
      } else {
        m = shiftl32(15 - i, 1);
      }

      n = m + 1;
      newKey[m] = newKey[n] = 0;

      for (var j = 0; j < 28; j++) {
        l = j + totrot[i];
        if (l < 28) {
          pcr[j] = pc1m[l];
        } else {
          pcr[j] = pc1m[l - 28];
        }
      }

      for (var j = 28; j < 56; j++) {
        l = j + totrot[i];
        if (l < 56) {
          pcr[j] = pc1m[l];
        } else {
          pcr[j] = pc1m[l - 28];
        }
      }

      for (var j = 0; j < 24; j++) {
        if (pcr[pc2[j]]) {
          newKey[m] |= bigbyte[j];
        }

        if (pcr[pc2[j + 24]]) {
          newKey[n] |= bigbyte[j];
        }
      }
    }

    //
    // store the processed key
    //
    for (var i = 0; i != 32; i += 2) {
      int i1, i2;

      i1 = newKey[i];
      i2 = newKey[i + 1];

      newKey[i] = (shiftl32(i1 & 0x00fc0000, 6)) |
          (shiftl32(i1 & 0x00000fc0, 10)) |
          (shiftr32(i2 & 0x00fc0000, 10)) |
          (shiftr32(i2 & 0x00000fc0, 6));

      newKey[i + 1] = (shiftl32(i1 & 0x0003f000, 12)) |
          (shiftl32(i1 & 0x0000003f, 16)) |
          (shiftr32(i2 & 0x0003f000, 4)) |
          (i2 & 0x0000003f);
    }

    return newKey;
  }

  void desFunc(
    List<int> wKey,
    Uint8List inp,
    int inOff,
    Uint8List out,
    int outOff,
  ) {
    int work, right, left;

    left = _bigEndianToInt(inp, inOff);
    right = _bigEndianToInt(inp, inOff + 4);

    work = ((shiftr32(left, 4)) ^ right) & 0x0f0f0f0f;
    right ^= work;
    left ^= shiftl32(work, 4);
    work = ((shiftr32(left, 16)) ^ right) & 0x0000ffff;
    right ^= work;
    left ^= shiftl32(work, 16);
    work = ((shiftr32(right, 2)) ^ left) & 0x33333333;
    left ^= work;
    right ^= shiftl32(work, 2);
    work = ((shiftr32(right, 8)) ^ left) & 0x00ff00ff;
    left ^= work;
    right ^= shiftl32(work, 8);
    right = (shiftl32(right, 1)) | (shiftr32(right, 31));
    work = (left ^ right) & 0xaaaaaaaa;
    left ^= work;
    right ^= work;
    left = (shiftl32(left, 1)) | (shiftr32(left, 31));

    for (var round = 0; round < 8; round++) {
      int fval;

      work = (shiftl32(right, 28)) | (shiftr32(right, 4));
      work ^= wKey[round * 4 + 0];
      fval = SP7[work & 0x3f];
      fval |= SP5[(shiftr32(work, 8)) & 0x3f];
      fval |= SP3[(shiftr32(work, 16)) & 0x3f];
      fval |= SP1[(shiftr32(work, 24)) & 0x3f];
      work = right ^ wKey[round * 4 + 1];
      fval |= SP8[work & 0x3f];
      fval |= SP6[(shiftr32(work, 8)) & 0x3f];
      fval |= SP4[(shiftr32(work, 16)) & 0x3f];
      fval |= SP2[(shiftr32(work, 24)) & 0x3f];
      left ^= fval;
      work = (shiftl32(left, 28)) | (shiftr32(left, 4));
      work ^= wKey[round * 4 + 2];
      fval = SP7[work & 0x3f];
      fval |= SP5[(shiftr32(work, 8)) & 0x3f];
      fval |= SP3[(shiftr32(work, 16)) & 0x3f];
      fval |= SP1[(shiftr32(work, 24)) & 0x3f];
      work = left ^ wKey[round * 4 + 3];
      fval |= SP8[work & 0x3f];
      fval |= SP6[(shiftr32(work, 8)) & 0x3f];
      fval |= SP4[(shiftr32(work, 16)) & 0x3f];
      fval |= SP2[(shiftr32(work, 24)) & 0x3f];
      right ^= fval;
    }
    right = (shiftl32(right, 31)) | (shiftr32(right, 1));
    work = (left ^ right) & 0xaaaaaaaa;
    left ^= work;
    right ^= work;
    left = (shiftl32(left, 31)) | (shiftr32(left, 1));
    work = ((shiftr32(left, 8)) ^ right) & 0x00ff00ff;
    right ^= work;
    left ^= shiftl32(work, 8);
    work = ((shiftr32(left, 2)) ^ right) & 0x33333333;
    right ^= work;
    left ^= shiftl32(work, 2);
    work = ((shiftr32(right, 16)) ^ left) & 0x0000ffff;
    left ^= work;
    right ^= shiftl32(work, 16);
    work = ((shiftr32(right, 4)) ^ left) & 0x0f0f0f0f;
    left ^= work;
    right ^= shiftl32(work, 4);

    _intToBigEndian(right, out, outOff);
    _intToBigEndian(left, out, outOff + 4);
  }

  void _intToBigEndian(int n, Uint8List bs, int off) {
    bs[off] = shiftr32(n, 24);
    bs[++off] = shiftr32(n, 16);
    bs[++off] = shiftr32(n, 8);
    bs[++off] = n;
  }

  int _bigEndianToInt(Uint8List bs, int off) {
    var n = shiftl32(bs[off], 24);
    n |= shiftl32(bs[++off] & 0xff, 16);
    n |= shiftl32(bs[++off] & 0xff, 8);
    n |= bs[++off] & 0xff;
    return n;
  }
}
